const fs = require('fs');
const request = require('request')

const RedisUtil = require("./RedisUtil");
const GiteeTokenUtil = require("./GiteeTokenUtil");
const CacheKeyUtil = require("./CacheKeyUtil");
const FileUtil = require("./FileUtil");

exports.parseStargazersUrl = async function (owner, repo, page) {
    page = !page ? 1 : page;
    const token = await GiteeTokenUtil.getAccessToken();
    const url = `https://gitee.com/api/v5/repos/${owner}/${repo}/stargazers?access_token=${token}&page=${page}&per_page=100`;
    console.log(url)
    return url;
}

exports.saveImgAndWriteToResponse = function (data, res) {
    res.set('content-type', "image/gif");//设置返回类型
    if (!data) {
        res.write("No data available");
        res.end();
        return;
    }
    const that = this
    const fileName = new Date().getTime() + Math.random() + '.png';
    fs.writeFile(fileName, new Buffer(data, 'base64'), function () {
        that.writeImgToResponse(fileName, res, true);
    });
}

exports.writeImgToResponse = function (filePath, res, delTempFile) {
    res.set('content-type', "image/gif");//设置返回类型
    if (!filePath) {
        res.write("No data available");
        res.end();
        return;
    }
    const that = this
    const stream = fs.createReadStream(filePath);
    let responseData = [];
    if (stream) {
        stream.on('data', function (chunk) {
            responseData.push(chunk);
        });
        stream.on('end', function () {
            if(delTempFile) {
                FileUtil.deleteFile(filePath);
            }
            res.write(Buffer.concat(responseData));
            res.end();
        });
    } else {
        if(delTempFile) {
            FileUtil.deleteFile(filePath);
        }
    }
}

exports.groupBy = function (array, f, obj) {
    var groups = obj || {};
    array.forEach(function (o) {
        var groupKey = f(o);
        // 某日的 star 数
        var starNum = groups[groupKey] || 0;
        groups[groupKey] = starNum + 1;
    });
    return groups;
}

exports.parseStarInfo = function (map) {
    console.log("=========", "合并 star 数量", "=========")
    let dateList = [];
    let data = [];
    if (map != null) {
        const starMap = map;
        // 累加后的 star 数
        let starTotal = 0;
        Object.keys(starMap).forEach(function (k, i) {
            // k 日的 star 数
            const num = parseInt(starMap[k]);

            // 将日期存入数组
            dateList.push(k);

            // 累加后的 star 数 = 已有 star 数 + k 日的 star 数
            starTotal = starTotal + num;
            // 将 k 日一共获取的 star 数存入 数组
            data.push(starTotal);
        })
    }

    return {
        dateList: dateList,
        data: data
    }
}

exports.getData = async function (that, owner, repo, page, callback, obj, expire) {
    const repoKey = CacheKeyUtil.repoKey(owner, repo);
    const maxPageKey = CacheKeyUtil.maxPageKey(repoKey);
    const totalKey = CacheKeyUtil.totalKey(repoKey);
    const starInfoCacheKey = CacheKeyUtil.starInfoKey(repoKey);
    const maxPage = parseInt(await RedisUtil.get(maxPageKey));
    let starTotal = await RedisUtil.get(totalKey)
    if (page > maxPage) {
        let starInfo = await RedisUtil.get(starInfoCacheKey)
        if (!starInfo) {
            starInfo = that.parseStarInfo(obj)
        }
        callback && callback({
            starTotal: starTotal,
            starInfo: starInfo
        });
        return;
    }
    console.log("请求 API，获取 " + repoKey + " 的第 " + page + " 页数据，共 " + maxPage + " 页数据")
    request(await that.parseStargazersUrl(owner, repo, page), function (err, response, body) {
        if (!err && response.statusCode === 200) {
            const starArray = JSON.parse(body);
            const map = that.groupBy(starArray, function (item) {
                return item.star_at.substr(0, 10)
            }, obj)
            RedisUtil.set(CacheKeyUtil.pageKey(repoKey, page), map, expire);

            var headers = response.headers;
            starTotal = parseInt(headers.total_count)
            var starPage = parseInt(headers.total_page)

            if (page <= starPage) {
                page = page + 1;
                if (page > starPage) {
                    const starInfo = that.parseStarInfo(map)
                    RedisUtil.set(starInfoCacheKey, starInfo, expire);
                    callback && callback({
                        starTotal: starTotal,
                        starInfo: starInfo
                    });
                    return;
                }
                RedisUtil.set(maxPageKey, starPage, expire);
                RedisUtil.set(totalKey, starTotal, expire);
                that.getData(that, owner, repo, page, callback, map, expire)
            } else {
                const starInfo = that.parseStarInfo(map)
                RedisUtil.set(starInfoCacheKey, starInfo, expire);
                callback && callback({
                    starTotal: starTotal,
                    starInfo: starInfo
                });
            }
        } else {
            const starInfo = that.parseStarInfo(obj)
            RedisUtil.set(starInfoCacheKey, starInfo, expire);
            callback && callback({
                starTotal: 0,
                starInfo: starInfo
            });
        }
    })
}

exports.getStarChartData = async function (owner, repo, page, obj, callback, expire) {
    const that = this

    const repoKey = CacheKeyUtil.repoKey(owner, repo);
    const totalKey = CacheKeyUtil.totalKey(repoKey);
    const loadKey = CacheKeyUtil.precessKey(repoKey);

    const processing = await RedisUtil.get(loadKey)

    const projectKey = CacheKeyUtil.trendKey.project(owner, repo)
    const userKey = CacheKeyUtil.trendKey.user(owner)
    const projectStarTimeKey = CacheKeyUtil.trendKey.startTime(owner, repo)


    const starInfoCacheKey = CacheKeyUtil.starInfoKey(repoKey);
    if (!processing) {

        // 用户的项目访问 +1
        // 用户的访问 = sum(用户名下所有项目的访问次数)
        RedisUtil.incr(projectKey, function (err, key) {
            // 记录使用的用户，以用户名为 key，一个用户对应多个项目
            RedisUtil.sadd(userKey, repo)
        })

        // 记录加载图表程序的开始时间
        RedisUtil.set(projectStarTimeKey, new Date().getTime())

        // 标记线程开始
        console.log("开始获取", repoKey, "的 star 数据，标记当前任务状态为开启...")
        await RedisUtil.set(loadKey, true, expire)

        let starInfo = await RedisUtil.get(starInfoCacheKey)
        if (starInfo && starInfo.data && starInfo.data.length > 0) {
            console.log(repoKey + " 有缓存, 返回缓存中的数据")
            let starTotal = await RedisUtil.get(totalKey)
            callback && callback({
                starTotal: starTotal,
                starInfo: starInfo
            });
            return;
        } else {
            console.log(repoKey + " 没有缓存，请求 Gitee API")
            await that.getData(that, owner, repo, page, callback, {}, expire);
            return;
        }
    }

    console.log("线程正在获取数据，不需要重复获取")

}

exports.request = function (url, callback, errorCallback) {
    request(url, function (err, response, body) {
        if (!err && response.statusCode === 200) {
            const starArray = JSON.parse(body);
            callback && callback(response, starArray)
        } else {
            errorCallback && errorCallback(JSON.parse(response.body).message);
        }
    })
}

exports.getRealExpireTime = function (expire) {
    // expire && parseInt(expire) < 3600 ? expire = 3600 : expire = parseInt(expire), console.log("启用自定义的数据缓存时间，当前设置的缓存时间为", expire, "s")
    if (expire) {
        expire = parseInt(expire);
        if (isNaN(expire) || expire < 3600) {
            console.error("自定义的数据缓存时间配置无效，使用系统级的缓存有效期配置（config.redis.expire）")
            return null;
        }
        console.log("启用自定义的数据缓存时间，当前设置的缓存时间为", expire, "s")
        return expire;
    }
    return null;
}

exports.downloadImg = async function (url, filePath, callback) {
    const writeStream = fs.createWriteStream(filePath);
    const readStream = request(url)
    readStream.pipe(writeStream);
    readStream.on('error', function(err) {
        console.log("error", err, url)
        callback && callback(err)
    })
    writeStream.on("finish", function(err) {
        writeStream.end();
        callback && callback(err)
    });
}
